/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.baatar.opt.genetic.set.covering.binary;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import org.baatar.opt.genetic.chromosome.IChromosome;
import org.baatar.opt.genetic.improve.IImprover;
import org.baatar.opt.genetic.set.meta.Column;
import org.baatar.opt.genetic.set.meta.DataSet;
import org.baatar.opt.genetic.set.meta.Row;
import org.baatar.util.RandomUtil;

/**
 *
 * @author Ba
 */
public final class SCImprover implements IImprover<Boolean, BitSet> {

    private int chromosomeLength = 0;
    private DataSet dataSet = null;

    public SCImprover(DataSet dataSet) {
        this.dataSet = dataSet;
        this.chromosomeLength = dataSet.columns.length;
    }

    @Override
    public void repair(IChromosome<Boolean, BitSet> chromosome) {

        SCChromosome scChromosome = (SCChromosome) chromosome;

        /**
         * Repair
         */
        for (int i = 0; i < scChromosome.colCount_Row.length; i++) {
            if (scChromosome.colCount_Row[i] == 0) {

                Row row = dataSet.rows[i];

                if (row.columns != null) {

                    if (row.columns.length > 0) {   //  If there is a column for the row.

                        int cSize = row.columns.length;

                        Column bestColumn = null;
                        int bestIntersectionSetSize = dataSet.rows.length * 10;
                        double bestColumnQuality = 0.0;

                        for (int j = 0; j < cSize; j++) {

                            Column column = row.columns[j];

                            int localIntersectionSetSize = 0;

                            int crlSize = column.rows.length;
                            for (int k = 0; k < crlSize; k++) {
                                Row iRow = column.rows[k];

                                int rowIndex = iRow.ndx;

                                if (scChromosome.colCount_Row[rowIndex] >= 1) {
                                    localIntersectionSetSize++;
                                }
                            }

                            if ((localIntersectionSetSize < bestIntersectionSetSize) ||
                                    ((localIntersectionSetSize == bestIntersectionSetSize) &&
                                    (column.quality > bestColumnQuality))) {

                                bestIntersectionSetSize = localIntersectionSetSize;
                                bestColumn = column;
                                bestColumnQuality = column.quality;
                            }
                        }

                        scChromosome.setGeneValue(bestColumn.ndx, true);
                    }
                }
            }
        }

        /**
         * Reduce redundancy
         */

        for (int i = 0; i < chromosomeLength; i++) {

            if (chromosome.getGeneValue(i)) {

                Column column = dataSet.columns[i];

                boolean canBeRemoved = true;

                int crlSize = column.rows.length;
                for (int k = 0; k < crlSize; k++) {
                    Row row = column.rows[k];

                    int rowIndex = row.ndx;

                    if (scChromosome.colCount_Row[rowIndex] <= 1) {
                        canBeRemoved = false;
                        break;
                    }
                }

                if (canBeRemoved) {
                    chromosome.setGeneValue(column.ndx, false);
                }
            }
        }
    }

//    @Override
//    public void runGreedyOperator(IChromosome<Boolean, BitSet> chromosome,
//                                    float greedRatio,
//                                    RandomUtil random) {
//
//        if (random.getRandDouble() < greedRatio) {
//            SCChromosome scChromosome = (SCChromosome) chromosome;
//
//            Column column = null;
//            double fitnessBefore = 0.0;
//            List<Column> repColList = new ArrayList<Column>();
//            boolean res = false;
//
//            int i = random.getRandNumber(chromosomeLength);
//            int j = chromosomeLength;
//            if (random.getRandDouble() > 0.5) {
//                j = i;
//                i = 0;
//            }
//
//            while (i < j) {
//
//                if (chromosome.getGeneValue(i)) {
//
//                    column = dataSet.columns[i];
//
//                    fitnessBefore = chromosome.getFitness();
//
//                    chromosome.setGeneValue(column.ndx, false);
//
//                    repColList.clear();
//                    res = getRepColumnList(scChromosome, column, repColList);
//
//                    if (!res || fitnessBefore < chromosome.getFitness()) {
//                        chromosome.setGeneValue(column.ndx, true);
//                        for (int r = 0; r < repColList.size(); r++) {
//                            chromosome.setGeneValue(repColList.get(r).ndx, false);
//                        }
//                    } else
//                        break;
//
//                }
//                i++;
//            }
//        }
//    }
//
//    public boolean getRepColumnList(SCChromosome chromosome, Column reservedCol, List<Column> repairList) {
//
//        for (int i = 0; i < chromosome.colCount_Row.length; i++) {
//            if (chromosome.colCount_Row[i] == 0) {
//
//                Row row = dataSet.rows[i];
//
//                if (row.columns != null) {  //  If there is at least one column for the row.
//                    if (row.columns.length > 0) {
//
//                        int cSize = row.columns.length;
//
//                        Column bestColumn = null;
//                        int bestIntersectionSetSize = dataSet.rows.length * 10;
//                        double bestColumnQuality = 0.0;
//
//                        for (int j = 0; j < cSize; j++) {
//
//                            Column column = row.columns[j];
//
//                            if (reservedCol.ndx != column.ndx) {
//
//                                int localIntersectionSetSize = 0;
//
//                                int crlSize = column.rows.length;
//                                for (int k = 0; k < crlSize; k++) {
//                                    Row iRow = column.rows[k];
//
//                                    int rowIndex = iRow.ndx;
//
//                                    if (chromosome.colCount_Row[rowIndex] >= 1) {
//                                        localIntersectionSetSize++;
//                                    }
//                                }
//
//                                if ((localIntersectionSetSize < bestIntersectionSetSize) ||
//                                        ((localIntersectionSetSize == bestIntersectionSetSize) &&
//                                        (column.quality > bestColumnQuality))) {
//
//                                    bestIntersectionSetSize = localIntersectionSetSize;
//                                    bestColumn = column;
//                                    bestColumnQuality = column.quality;
//                                }
//                            }
//                        }
//
//                        if (bestColumn != null) {
//                            repairList.add(bestColumn);
//                            chromosome.setGeneValue(bestColumn.ndx, true);
//                        } else {
//                            return false;
//                        }
//                    }
//                }
//            }
//        }
//        return true;
//    }

    @Override
    public void runGreedyOperator(IChromosome<Boolean, BitSet> chromosome,
                                    float greedRatio,
                                    RandomUtil random) {
        if (random.getRandDouble() < greedRatio) {

            int level = 1;
            if (random.getRandDouble() > 0.95)
                level = 2;
            else if (random.getRandDouble() > 0.99)
                level = 3;

            SCChromosome scChromosome = (SCChromosome) chromosome;

            Column column = null;
            double fitnessBefore = 0.0;
            List<Column> repColList = new ArrayList<Column>();
            boolean res = false;

            int i = random.getRandNumber(chromosomeLength);
            int j = chromosomeLength;
            if (random.getRandDouble() > 0.5) {
                j = i;
                i = 0;
            }

            while (i < j) {

                if (chromosome.getGeneValue(i)) {

                    Column columnFound = dataSet.columns[i];

                    ArrayList<Column> columns = new ArrayList<Column>();

                    columns.add(columnFound);

                    while (level == 1 || getGeneCombinations(scChromosome, level, columns)) {

                        fitnessBefore = chromosome.getFitness();

                        for (int p = 0; p < columns.size(); p++) {
                            chromosome.setGeneValue(columns.get(p).ndx, false);
                        }

                        ArrayList<Column> repairList = new ArrayList<Column>();
                        res = getRePairList(scChromosome, columns, repairList);

                        if (!res || fitnessBefore < chromosome.getFitness()) {
                            for (int p = 0; p < columns.size(); p++) {
                                chromosome.setGeneValue(columns.get(p).ndx, true);
                            }
                            for (int r = 0; r < repairList.size(); r++) {
                                chromosome.setGeneValue(repairList.get(r).ndx, false);
                            }
                        } else {
                            break;
                        }

                        if (level == 1) {
                            break;
                        }
                    }
                }
                i++;
            }
        }
    }

    private boolean getGeneCombinations(SCChromosome chromosome, int level, ArrayList<Column> columns) {

        Column lastCol = columns.get(columns.size() - 1);

        if (columns.size() == level) {
            columns.remove(level - 1);
        }

        while (columns.size() < level) {
            boolean ok = false;
            for (int i = lastCol.ndx + 1; i < chromosomeLength; i++) {
                if (chromosome.getGeneValue(i)) {
                    Column foundCol = dataSet.columns[i];
                    ok = true;
                    columns.add(foundCol);
                    lastCol = foundCol;
                    break;
                }
            }
            if (!ok) {
                columns.remove(columns.size() - 1);
                if (columns.size() <= 1) {
                    return false;
                }
                lastCol = columns.get(columns.size() - 1);
                columns.remove(columns.size() - 1);
            }
        }
        return true;
    }

    public boolean getRePairList(SCChromosome chromosome, ArrayList<Column> reservedCols, ArrayList<Column> repairList) {

        for (int i = 0; i < chromosome.colCount_Row.length; i++) {
            if (chromosome.colCount_Row[i] == 0) {

                Row aRow = dataSet.rows[i];

                if ((aRow.columns != null) && (aRow.columns.length > 0)) {

                    Column[] rowColumnList = aRow.columns;

                    if (rowColumnList != null) {
                        int cSize = rowColumnList.length;

                        Column bestColumn = null;
                        int bestIntersectionSetSize = dataSet.rows.length * 10;
                        double bestPairRatio = 0.0;

                        for (int j = 0; j < cSize; j++) {

                            Column column = rowColumnList[j];

                            if (reservedCols.indexOf(column) == -1) {

                                int localIntersectionSetSize = 0;

                                Row[] crl = dataSet.columns[column.ndx].rows;

                                int pflSize = crl.length;
                                for (int k = 0; k < pflSize; k++) {
                                    Row row = crl[k];

                                    if (chromosome.colCount_Row[row.ndx] >= 1) {
                                        localIntersectionSetSize++;
                                    }
                                }

                                if ((localIntersectionSetSize < bestIntersectionSetSize) ||
                                        ((localIntersectionSetSize == bestIntersectionSetSize) &&
                                        (column.quality > bestPairRatio))) {

                                    bestIntersectionSetSize = localIntersectionSetSize;
                                    bestColumn = column;
                                    bestPairRatio = column.quality;
                                }
                            }
                        }

                        if (bestColumn != null) {
                            repairList.add(bestColumn);
                            chromosome.setGeneValue(bestColumn.ndx, true);
                        } else {
                            return false;
                        }
                    }
                }
            }
        }
        return true;
    }

    /**
     * Clones the this object.
     * 
     * @return Object new cloned object.
     * 
     * @throws CloneNotSupportedException is trown if this object doesnt
     * implements Cloneable interface.
     */
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public boolean postProcess(IChromosome<Boolean, BitSet> chromosome) {
        return false;
    }
}
